package com.ooono.factorytest.items;

import static android.location.GnssStatus.CONSTELLATION_BEIDOU;
import static android.location.GnssStatus.CONSTELLATION_GPS;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.location.GnssStatus;
import android.location.LocationManager;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.ooono.factorytest.R;
import com.ooono.factorytest.base.BaseActivity;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import mcs8.mdm.PolicyManager;

public class GpsCameraTestActivity extends BaseActivity {

    private static final String TAG = "GpsCameraTestActivity";

    public static final String KEY = "gps_camera";

    private TextView gpsInfoTv;
    private TextView gpsPositionTv;

    private String beidou = "1";
    private Button openLocationBtn;
    private Handler mHandler;

    private TextureView cameraPreviewTv;
    private CameraManager mCameraManager;
    private CameraDevice mCameraDevice;
    private CameraCaptureSession mCameraCaptureSession;
    private CameraDevice.StateCallback mCameraDeviceStateCallback;
    private CameraCaptureSession.StateCallback mSessionStateCallback;
    private CameraCaptureSession.CaptureCallback mSessionCaptureCallback;
    private CaptureRequest.Builder mPreviewCaptureRequest;
    private MediaRecorder mMediaRecorder;
    private final String mCurrentSelectCamera = "0";
    private Handler mChildHandler;

    GpsAdapter mGpsAdapter;
    // 卫星信号
    List<SatellitesLiteInfo> mAdapterData = new ArrayList<>();

    private String gpsTestInfo;
    private boolean isRecording = false;           // false表示没有录像，点击开始；true表示正在录像，点击暂停

    @Override
    public int getTestTitle() {
        return R.string.gps_test_prompt;
    }

    @Override
    public String getTestKey() {
        return KEY;
    }

    @Override
    public int getContentLayout() {
        return R.layout.content_gps_camera_test;
    }

    @Override
    public void initViews() {
        cameraPreviewTv = findViewById(R.id.tv_camera_preview);

        middleBtn.setVisibility(View.VISIBLE);
        middleBtn.setText("开始录像");
        middleBtn.setOnClickListener(v -> {
            // 开始录像
            if (isRecording) {
                isRecording = false;
                middleBtn.setText("开始录像");
                stopRecorder();
            } else {
                isRecording = true;
                middleBtn.setText("停止录像");
                startRecorder();
            }
        });

        HandlerThread handlerThread = new HandlerThread("GpsCamera2");
        handlerThread.start();
        mChildHandler = new Handler(handlerThread.getLooper());

        if (cameraPreviewTv != null) {
            cameraPreviewTv.setSurfaceTextureListener(mTextureListener);
        }
        mMediaRecorder = new MediaRecorder();

        initCameraDeviceStateCallback();
        initSessionStateCallback();
        initSessionCaptureCallback();

        mHandler = new Handler();
        gpsTestInfo = getResources().getString(R.string.gps_test_info);
        gpsInfoTv = findViewById(R.id.tv_gps_info);
        gpsPositionTv = findViewById(R.id.gps_test_position_tv);
        openLocationBtn = findViewById(R.id.btn_gps_test_open_location);
        // 进入界面先停用
        PolicyManager.setDriveNodeValue("/sys/class/leds/alermled/huada_gps", "0");
        String beidouChip = PolicyManager.getSystemProperties("ro.beidou.chip", "0");
        if ("1".equals(beidouChip)) {
            openLocationBtn.setVisibility(View.VISIBLE);
        }
        openLocationBtn.setOnClickListener(v -> {
            // 开关北头
            if ("1".equals(beidou)) {
                beidou = "0";
                openLocationBtn.setText(R.string.gps_test_open);
            } else {
                beidou = "1";
                openLocationBtn.setText(R.string.gps_test_close);
            }
            PolicyManager.setDriveNodeValue("/sys/class/leds/alermled/huada_gps", beidou);
        });

        ListView gpsSnrListLv = findViewById(R.id.lv_gps_snr_list);
        // listview :显示GPS检测列表
        mGpsAdapter = new GpsAdapter(this, R.layout.gps_camera_test_list_item);
        gpsSnrListLv.setAdapter(mGpsAdapter);
    }

    @SuppressLint({"SetTextI18n", "SimpleDateFormat"})
    @Override
    protected void onResume() {
        super.onResume();

        beidou = PolicyManager.getDriveNodeValue("/sys/class/leds/alermled/huada_gps");
        if ("1".equals(beidou)) {
            openLocationBtn.setText(R.string.gps_test_close);
        } else {
            openLocationBtn.setText(R.string.gps_test_open);
        }

        boolean enabled = Settings.Secure.isLocationProviderEnabled(getContentResolver(), LocationManager.GPS_PROVIDER);
        if (!enabled) {
            Settings.Secure.setLocationProviderEnabled(getContentResolver(), LocationManager.GPS_PROVIDER, true);
        }

        LocationManager mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        Log.d(TAG, "mLocationManager:" + mLocationManager);
        if (mLocationManager != null) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 5, location -> {
                Log.d(TAG, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "当前位置 longitude : " + location.getLongitude() +
                        "latitude : " + location.getLatitude());
                mHandler.post(() -> {
                    String longitude = latLng2Dfm(location.getLongitude());
                    String latitude = latLng2Dfm(location.getLatitude());
                    gpsPositionTv.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\n" + longitude +
                            " - " + latitude);
                });
            });
            gpsInfoTv.setText(R.string.gps_test_info);
        } else {
            gpsInfoTv.setText(R.string.gps_test_get_location_fail);
            return;
        }

        Resources resources = getResources();

        mLocationManager.registerGnssStatusCallback(new GnssStatus.Callback() {

            @Override
            public void onSatelliteStatusChanged(@NonNull GnssStatus status) {
                super.onSatelliteStatusChanged(status);
                int satelliteCount = status.getSatelliteCount();
                Log.d(TAG, ">>>--------------------" + satelliteCount);
                int bdCount = 0;
                int gpsCount = 0;
                int otherCount = 0;
                mAdapterData.clear();
                for (int i = 0; i < satelliteCount; i++) {
                    SatellitesLiteInfo satellitesLiteInfo = new SatellitesLiteInfo();
                    satellitesLiteInfo.svid = status.getSvid(i);
                    satellitesLiteInfo.cn0DbHz = status.getCn0DbHz(i);
                    if (satellitesLiteInfo.cn0DbHz <= 0) {
                        continue;
                    }
                    switch (status.getConstellationType(i)) {
                        case CONSTELLATION_GPS:
                            satellitesLiteInfo.type = resources.getString(R.string.gps_test_type_gps);
                            gpsCount++;
                            break;
                        case CONSTELLATION_BEIDOU:
                            satellitesLiteInfo.type = resources.getString(R.string.gps_test_type_bd);
                            bdCount++;
                            break;
                        default:
                            satellitesLiteInfo.type = resources.getString(R.string.gps_test_type_other);
                            otherCount++;
                            break;
                    }
                    mAdapterData.add(satellitesLiteInfo);
                }
                gpsInfoTv.setText(String.format(gpsTestInfo, satelliteCount, bdCount, gpsCount, otherCount));
                mGpsAdapter.notifyDataSetChanged();
            }
        });
    }

    public String latLng2Dfm(double du) {
        int du1 = (int) du;
        double tp = (du - du1) * 60;
        int fen = (int) tp;
        String miao = String.format("%.2f", Math.abs(((tp - fen) * 60)));
        return du1 + "°" + Math.abs(fen) + "'" + miao + "\"";
    }

    private void startRecorder() {
        config();
        mMediaRecorder.start();
    }

    private void stopRecorder() {
        mMediaRecorder.stop();
        mMediaRecorder.reset();

        // 其次把文件插入到系统图库
        try {
            MediaStore.Images.Media.insertImage(getContentResolver(),
                    recorderFile.getAbsolutePath(), recorderFileName, null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 最后通知图库更新
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri uri = Uri.fromFile(recorderFile);
        intent.setData(uri);
        sendBroadcast(intent);
    }

    private String recorderFileName;
    private File recorderFile;

    /**
     * 配置录制视频相关数据
     */
    private void configMediaRecorder() {
        //设置视频存储路径
        File filePath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) + File.separator + "factory");
        if (!filePath.exists()) {
            //多级文件夹的创建
            filePath.mkdirs();
        }
        recorderFileName = "video_" + System.currentTimeMillis() + ".mp4";
        recorderFile = new File(filePath, recorderFileName);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置音频来源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);//设置视频来源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//设置输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频编码格式，请注意这里使用默认，实际app项目需要考虑兼容问题，应该选择AAC
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);//设置视频编码格式，请注意这里使用默认，实际app项目需要考虑兼容问题，应该选择H264
        mMediaRecorder.setVideoEncodingBitRate(8 * 1024 * 1920);//设置比特率 一般是 1*分辨率 到 10*分辨率 之间波动。比特率越大视频越清晰但是视频文件也越大。
        mMediaRecorder.setVideoFrameRate(30);//设置帧数 选择 30即可， 过大帧数也会让视频文件更大当然也会更流畅，但是没有多少实际提升。人眼极限也就30帧了。
        Size size = getMatchingSize2();
        mMediaRecorder.setVideoSize(size.getWidth(), size.getHeight());
        mMediaRecorder.setOrientationHint(0);
        Surface surface = new Surface(cameraPreviewTv.getSurfaceTexture());
        mMediaRecorder.setPreviewDisplay(surface);
        mMediaRecorder.setOutputFile(recorderFile.getAbsolutePath());
        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 重新配置录制视频时的CameraCaptureSession
     */
    private void config() {
        try {
            mCameraCaptureSession.stopRepeating();//停止预览，准备切换到录制视频
            mCameraCaptureSession.close();//关闭预览的会话，需要重新创建录制视频的会话
            mCameraCaptureSession = null;
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        configMediaRecorder();
        Size cameraSize = getMatchingSize2();
        SurfaceTexture surfaceTexture = cameraPreviewTv.getSurfaceTexture();
        surfaceTexture.setDefaultBufferSize(cameraSize.getWidth(), cameraSize.getHeight());
        Surface previewSurface = new Surface(surfaceTexture);
        Surface recorderSurface = mMediaRecorder.getSurface();//从获取录制视频需要的Surface
        try {
            mPreviewCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            mPreviewCaptureRequest.addTarget(previewSurface);
            mPreviewCaptureRequest.addTarget(recorderSurface);
            //请注意这里设置了Arrays.asList(previewSurface,recorderSurface) 2个Surface，很好理解录制视频也需要有画面预览，第一个是预览的Surface，第二个是录制视频使用的Surface
            mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, recorderSurface), mSessionStateCallback, mChildHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void initCameraDeviceStateCallback() {
        mCameraDeviceStateCallback = new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                //摄像头被打开
                try {
                    mCameraDevice = camera;
                    Size cameraSize = getMatchingSize2();//计算获取需要的摄像头分辨率
                    SurfaceTexture surfaceTexture = cameraPreviewTv.getSurfaceTexture();//得到纹理
                    surfaceTexture.setDefaultBufferSize(cameraSize.getWidth(), cameraSize.getHeight());
                    Surface previewSurface = new Surface(surfaceTexture);
                    mPreviewCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                    mPreviewCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                    mPreviewCaptureRequest.addTarget(previewSurface);
                    mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), mSessionStateCallback, mChildHandler);//创建数据捕获会话，用于摄像头画面预览，这里需要等待mSessionStateCallback回调
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onDisconnected(@NonNull CameraDevice camera) {
                //摄像头断开
            }

            @Override
            public void onError(@NonNull CameraDevice camera, int error) {
                //异常
            }
        };
    }

    private void initSessionStateCallback() {
        mSessionStateCallback = new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                mCameraCaptureSession = session;
                try {
                    //执行重复获取数据请求，等于一直获取数据呈现预览画面，mSessionCaptureCallback会返回此次操作的信息回调
                    mCameraCaptureSession.setRepeatingRequest(mPreviewCaptureRequest.build(), mSessionCaptureCallback, mChildHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }

            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {

            }
        };
    }

    private void initSessionCaptureCallback() {
        mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
                super.onCaptureStarted(session, request, timestamp, frameNumber);
            }

            @Override
            public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
                super.onCaptureProgressed(session, request, partialResult);
            }

            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
            }

            @Override
            public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                super.onCaptureFailed(session, request, failure);
            }
        };
    }

    /**
     * 计算需要的使用的摄像头分辨率
     *
     * @return
     */
    private Size getMatchingSize2() {
        Size selectSize = null;
        try {
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentSelectCamera);
            StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
            DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); //因为我这里是将预览铺满屏幕,所以直接获取屏幕分辨率
            int deviceWidth = displayMetrics.widthPixels; //屏幕分辨率宽
            int deviceHeigh = displayMetrics.heightPixels; //屏幕分辨率高
            Log.e(TAG, "getMatchingSize2: 屏幕密度宽度=" + deviceWidth);
            Log.e(TAG, "getMatchingSize2: 屏幕密度高度=" + deviceHeigh);
            /**
             * 循环40次,让宽度范围从最小逐步增加,找到最符合屏幕宽度的分辨率,
             * 你要是不放心那就增加循环,肯定会找到一个分辨率,不会出现此方法返回一个null的Size的情况
             * ,但是循环越大后获取的分辨率就越不匹配
             */
            for (int j = 1; j < 41; j++) {
                for (int i = 0; i < sizes.length; i++) { //遍历所有Size
                    Size itemSize = sizes[i];
                    Log.e(TAG, "当前itemSize 宽=" + itemSize.getWidth() + "高=" + itemSize.getHeight());
                    //判断当前Size高度小于屏幕宽度+j*5  &&  判断当前Size高度大于屏幕宽度-j*5  &&  判断当前Size宽度小于当前屏幕高度
                    if (itemSize.getHeight() < (deviceWidth + j * 5) && itemSize.getHeight() > (deviceWidth - j * 5)) {
                        if (selectSize != null) { //如果之前已经找到一个匹配的宽度
                            if (Math.abs(deviceHeigh - itemSize.getWidth()) < Math.abs(deviceHeigh - selectSize.getWidth())) { //求绝对值算出最接近设备高度的尺寸
                                selectSize = itemSize;
                                continue;
                            }
                        } else {
                            selectSize = itemSize;
                        }

                    }
                }
                if (selectSize != null) { //如果不等于null 说明已经找到了 跳出循环
                    break;
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        Log.e(TAG, "getMatchingSize2: 选择的分辨率宽度=" + selectSize.getWidth());
        Log.e(TAG, "getMatchingSize2: 选择的分辨率高度=" + selectSize.getHeight());
        return selectSize;
    }


    private final TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
            //可以使用纹理
            mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

            openCamera();
        }

        @Override
        public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {

        }

        @Override
        public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {

        }
    };

    /**
     * 打开摄像头，这里打开摄像头后，我们需要等待mCameraDeviceStateCallback的回调
     */
    @SuppressLint("MissingPermission")
    private void openCamera() {
        try {
            mCameraManager.openCamera(mCurrentSelectCamera, mCameraDeviceStateCallback, mChildHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    static class SatellitesLiteInfo {
        String type;
        int svid;
        float cn0DbHz;
    }

    private class GpsAdapter extends BaseAdapter {
        private final int sourceId;
        private final LayoutInflater inflater;

        public GpsAdapter(Context context, int sourceId) {
            inflater = LayoutInflater.from(context);
            this.sourceId = sourceId;
        }

        @Override
        public int getCount() {
            return mAdapterData.size();
        }

        @Override
        public Object getItem(int position) {
            return mAdapterData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @SuppressLint("SetTextI18n")
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                convertView = inflater.inflate(sourceId, null);
                holder = new ViewHolder();
                holder.listItemTypeTv = convertView.findViewById(R.id.gps_test_list_item_type);
                holder.listItemSnrTv = convertView.findViewById(R.id.gps_test_list_item_snr);
                holder.listItemResultTv = convertView.findViewById(R.id.gps_test_list_item_result);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.listItemTypeTv.setText(mAdapterData.get(position).type + "-" + mAdapterData.get(position).svid);
            float cn0DbHz = mAdapterData.get(position).cn0DbHz;
            holder.listItemSnrTv.setText(cn0DbHz + "");
            if (cn0DbHz < 20) {
                // 弱
                holder.listItemResultTv.setText("较弱");
                holder.listItemResultTv.setTextColor(getResources().getColor(R.color.red));
            } else if (cn0DbHz < 30) {
                // 一般
                holder.listItemResultTv.setText("一般");
                holder.listItemResultTv.setTextColor(getResources().getColor(R.color.yellow));
            } else {
                // 强
                holder.listItemResultTv.setText("较强");
                holder.listItemResultTv.setTextColor(getResources().getColor(R.color.green));
            }

            return convertView;
        }

        private final class ViewHolder {
            TextView listItemTypeTv;
            TextView listItemSnrTv;
            TextView listItemResultTv;
        }
    }

}